`timescale 1ns / 1ps
/******************************************************************************
*                                                                             *
* UTICA softcore v0.1                                                         *
*                                                                             *
* Copyright (c) 2012 Andrew D. Zonenberg                                      *
* All rights reserved.                                                        *
*                                                                             *
* Redistribution and use in source and binary forms, with or without modifi-  *
* cation, are permitted provided that the following conditions are met:       *
*                                                                             *
*    * Redistributions of source code must retain the above copyright notice  *
*      this list of conditions and the following disclaimer.                  *
*                                                                             *
*    * Redistributions in binary form must reproduce the above copyright      *
*      notice, this list of conditions and the following disclaimer in the    *
*      documentation and/or other materials provided with the distribution.   *
*                                                                             *
*    * Neither the name of the author nor the names of any contributors may be*
*      used to endorse or promote products derived from this software without *
*      specific prior written permission.                                     *
*                                                                             *
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS "AS IS" AND ANY EXPRESS OR IMPLIED *
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF        *
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN     *
* NO EVENT SHALL THE AUTHORS BE HELD LIABLE FOR ANY DIRECT, INDIRECT,         *
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT    *
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,   *
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY       *
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT         *
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF    *
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.           *
*                                                                             *
******************************************************************************/

/**
	@file TopLevel.v
	@author Andrew D. Zonenberg
	@brief Top-level module for the SoC
 */
module TopLevel(
	clk_20mhz,
	leds, buttons,
	uart_tx, uart_rx,
	spi_sck, spi_cs_n, spi_data,
	i2c_sda, i2c_scl,
	gpio,
	mcb3_dram_dq, mcb3_dram_a, mcb3_dram_ba, mcb3_dram_cke, mcb3_dram_ras_n, mcb3_dram_cas_n, mcb3_dram_we_n,
	mcb3_dram_dm, mcb3_rzq, mcb3_dram_dqs, mcb3_dram_ck, mcb3_dram_ck_n
	
    );

	////////////////////////////////////////////////////////////////////////////////////////////////
	// IO declarations
	input wire clk_20mhz;
		
	//LEDs
	output wire[7:0] leds;
	
	//Buttons
	input wire[3:0] buttons;
	
	//UART	
	output wire uart_tx;
	input wire uart_rx;
	
	//SPI bus to NOR flash
	output wire spi_sck;
	output wire spi_cs_n;
	inout wire[3:0] spi_data;	
	
	//I2C bus
	inout wire i2c_sda;
	output wire i2c_scl;
	
	//GPIO
	inout wire[19:0] gpio;
	
	//Memory controller
	inout wire[7:0] mcb3_dram_dq;
	output wire[12:0] mcb3_dram_a;
	output wire[1:0] mcb3_dram_ba;
	output wire mcb3_dram_cke;
	output wire mcb3_dram_ras_n;
	output wire mcb3_dram_cas_n;
	output wire mcb3_dram_we_n;
	output wire mcb3_dram_dm;
	inout wire mcb3_rzq;
	output wire mcb3_dram_dqs;
	output wire mcb3_dram_ck;
	output wire mcb3_dram_ck_n;
	
	////////////////////////////////////////////////////////////////////////////////////////////////
	// Clock management
	
	wire clk_20mhz_ibufg;
	wire clk_ddr_p;
	wire clk_ddr_n;
	wire clk_ddr_p_raw;
	wire clk_ddr_n_raw;
	wire clk_mcb_calib;
	wire clk;
	wire main_clk_pll_reset;
	wire main_clk_pll_locked;
	
	//TODO: real reset here!
	assign main_clk_pll_reset = 0;
	
	CoreClockManagement clkmgr (
		.clk_20mhz(clk_20mhz), 
		.clk_20mhz_ibufg(clk_20mhz_ibufg), 
		.clk_ddr_p(clk_ddr_p), 
		.clk_ddr_n(clk_ddr_n), 
		.clk_mcb_calib(clk_mcb_calib),
		.clk_ddr_p_raw(clk_ddr_p_raw), 
		.clk_ddr_n_raw(clk_ddr_n_raw), 
		.clk(clk), 
		.main_clk_pll_reset(main_clk_pll_reset), 
		.main_clk_pll_locked(main_clk_pll_locked)
		);
		
	////////////////////////////////////////////////////////////////////////////////////////////////
	// The CPU
	
	wire hold;
	
	wire irq;
	wire[3:0] vector;
	
	wire[31:0] ibus_addr;
	wire ibus_rd;
	wire[31:0] ibus_din;
	wire ibus_dinok;
	
	wire[31:0] dbus_addr;
	wire dbus_rd;
	wire dbus_wr;
	wire[31:0] dbus_din;
	wire[31:0] dbus_dout; 
	wire[3:0] dbus_wmask;
	wire dbus_dinok;
	
	wire dram_calib_done;
	
	//Debug
	assign hold = !dram_calib_done;
	assign irq = 0;
	assign vector = 0;

	wire ram_fifo_full;

	UticaCPU cpu (
		.clk(clk), 
		.hold(hold), 
		.irq(irq), 
		.vector(vector), 
		.ibus_addr(ibus_addr), 
		.ibus_rd(ibus_rd), 
		.ibus_din(ibus_din), 
		.ibus_dinok(ibus_dinok & !ram_fifo_full), 	//if RAM fifo is full, force the CPU to stall fetches
		.dbus_addr(dbus_addr), 
		.dbus_rd(dbus_rd), 
		.dbus_wr(dbus_wr), 
		.dbus_dout(dbus_dout), 
		.dbus_wmask(dbus_wmask),
		.dbus_din(dbus_din), 
		.dbus_dinok(dbus_dinok)
	);
	
	////////////////////////////////////////////////////////////////////////////////////////////////
	// L1 caches
	
	wire[31:0] dbus_l2_addr;
	wire dbus_l2_rd;
	wire dbus_l2_wr;
	wire[31:0] dbus_l2_dout;
	wire[31:0] dbus_l2_din;
	wire[3:0] dbus_l2_wmask;
	wire dbus_l2_dinok;
	
	wire[31:0] ibus_l2_addr;
	wire ibus_l2_rd;
	//wire ibus_l2_wr;
	wire[31:0] ibus_l2_dout;
	//wire[31:0] ibus_l2_din;
	wire ibus_l2_dinok;
	
	L1Cache iside_l1cache (
		.clk(clk), 
		.cpu_addr(ibus_addr), 
		.cpu_rd(ibus_rd), 
		.cpu_wr(1'b0), 
		.cpu_dout(32'h00000000), 
		.cpu_wmask(4'b0),
		.cpu_din(ibus_din), 
		.cpu_dinok(ibus_dinok), 
		.l2_addr(ibus_l2_addr), 
		.l2_rd(ibus_l2_rd), 
		.l2_wr(ibus_l2_wr), 
		.l2_dout(ibus_l2_dout), 
		//.l2_wmask(ibus_l2_wmask),
		//.l2_din(ibus_l2_din), 
		.l2_dinok(ibus_l2_dinok)
		);
	
	L1Cache dside_l1cache (
		.clk(clk), 
		.cpu_addr(dbus_addr), 
		.cpu_rd(dbus_rd), 
		.cpu_wr(dbus_wr), 
		.cpu_dout(dbus_dout), 
		.cpu_wmask(dbus_wmask),
		.cpu_din(dbus_din), 
		.cpu_dinok(dbus_dinok), 
		.l2_addr(dbus_l2_addr), 
		.l2_rd(dbus_l2_rd), 
		.l2_wr(dbus_l2_wr), 
		.l2_dout(dbus_l2_dout), 
		.l2_wmask(dbus_l2_wmask),
		.l2_din(dbus_l2_din), 
		.l2_dinok(dbus_l2_dinok)
		);

	////////////////////////////////////////////////////////////////////////////////////////////////
	// L2 cache
	
	wire[31:0] mmu_addr;
	wire mmu_rd;
	wire mmu_wr;
	wire[31:0] mmu_dout;
	wire[31:0] mmu_din;
	wire[3:0] mmu_wmask;
	wire mmu_dinok;
	
	L2Cache l2cache (
		.clk(clk), 
		.iside_addr(ibus_l2_addr), 
		.iside_rd(ibus_l2_rd), 
		.iside_dout(ibus_l2_dout), 
		.iside_dinok(ibus_l2_dinok), 
		.dside_addr(dbus_l2_addr), 
		.dside_rd(dbus_l2_rd), 
		.dside_wr(dbus_l2_wr), 
		.dside_dout(dbus_l2_dout), 
		.dside_wmask(dbus_l2_wmask), 
		.dside_din(dbus_l2_din), 
		.dside_dinok(dbus_l2_dinok), 
		.mmu_addr(mmu_addr), 
		.mmu_rd(mmu_rd), 
		.mmu_wr(mmu_wr), 
		.mmu_dout(mmu_dout), 
		.mmu_wmask(mmu_wmask),
		.mmu_din(mmu_din), 
		.mmu_dinok(mmu_dinok)
		);

	////////////////////////////////////////////////////////////////////////////////////////////////
	// The MMU
	
	wire[31:0] norflash_addr;
	wire[31:0] norflash_din;
	wire[31:0] norflash_dout;
	wire[3:0] norflash_wmask;
	wire norflash_wr;
	wire norflash_rd;
	wire norflash_dinok;
	
	wire[31:0] uart_addr;
	wire[31:0] uart_din;
	wire[31:0] uart_dout;
	wire[3:0] uart_wmask;
	wire uart_wr;
	wire uart_rd;
	wire uart_dinok;
	
	wire[31:0] led_addr;
	wire[31:0] led_din;
	wire[7:0] led_dout;
	wire[3:0] led_wmask;
	wire led_wr;
	wire led_rd;
	wire led_dinok;
	
	wire[31:0] btn_addr;
	wire[31:0] btn_din;
	wire[7:0] btn_dout;
	wire[3:0] btn_wmask;
	wire btn_wr;
	wire btn_rd;
	wire btn_dinok;
	
	wire[31:0] ram_addr;
	wire[31:0] ram_din;
	wire[31:0] ram_dout;
	wire[3:0] ram_wmask;
	wire ram_wr;
	wire ram_rd;
	wire ram_dinok;
	
	MMU mmu (
		.clk(clk), 
		
		.mmu_addr(mmu_addr), 
		.mmu_din(mmu_din), 
		.mmu_dout(mmu_dout), 
		.mmu_wr(mmu_wr), 
		.mmu_rd(mmu_rd), 
		.mmu_dinok(mmu_dinok), 
		.mmu_wmask(mmu_wmask),

		.norflash_addr(norflash_addr), 
		.norflash_din(norflash_din), 
		.norflash_dout(norflash_dout), 
		.norflash_wmask(norflash_wmask),
		.norflash_wr(norflash_wr), 
		.norflash_rd(norflash_rd), 
		.norflash_dinok(norflash_dinok),
		
		.uart_addr(uart_addr), 
		.uart_din(uart_din), 
		.uart_dout(uart_dout), 
		.uart_wmask(uart_wmask),
		.uart_wr(uart_wr), 
		.uart_rd(uart_rd), 
		.uart_dinok(uart_dinok),
		
		.led_addr(led_addr), 
		.led_din(led_din), 
		.led_dout(led_dout), 
		.led_wmask(led_wmask),
		.led_wr(led_wr), 
		.led_rd(led_rd), 
		.led_dinok(led_dinok),
		
		.btn_addr(btn_addr), 
		.btn_din(btn_din), 
		.btn_dout(btn_dout), 
		.btn_wmask(btn_wmask),
		.btn_wr(btn_wr), 
		.btn_rd(btn_rd), 
		.btn_dinok(btn_dinok),
		
		.ram_addr(ram_addr), 
		.ram_din(ram_din), 
		.ram_dout(ram_dout), 
		.ram_wmask(ram_wmask),
		.ram_wr(ram_wr), 
		.ram_rd(ram_rd), 
		.ram_dinok(ram_dinok)		
		);
	
	////////////////////////////////////////////////////////////////////////////////////////////////
	// Peripherals
	
	LedBank ledbank (
		.clk(clk), 
		.addr(led_addr), 
		.wr(led_wr), 
		.rd(led_rd), 
		.din(led_din), 
		.wmask(led_wmask),
		.dout(led_dout), 
		.doutok(led_dinok), 
		.leds(leds)
		);
	
	MemoryMappedUART uart (
		.clk(clk), 
		.addr(uart_addr), 
		.wr(uart_wr), 
		.rd(uart_rd), 
		.din(uart_din), 
		.wmask(uart_wmask),
		.dout(uart_dout), 
		.doutok(uart_dinok),
		.tx(uart_tx), 
		.rx(uart_rx)
		);
		
	ButtonArray button_arr (
		.clk(clk), 
		.buttons(buttons), 
		.addr(btn_addr), 
		.wr(btn_wr), 
		.rd(btn_rd), 
		.din(btn_din), 
		.dout(btn_dout), 
		.wmask(btn_wmask), 
		.doutok(btn_doutok)
		);
	
	//NOR flash controller
	MemoryMappedNorFlashController nor_controller (
		.clk(clk), 
		.addr(norflash_addr), 
		.wr(norflash_wr), 
		.rd(norflash_rd), 
		.din(norflash_din), 
		.wmask(norflash_wmask), 
		.dout(norflash_dout), 
		.doutok(norflash_dinok), 
		.spi_sck(spi_sck), 
		.spi_cs_n(spi_cs_n), 
		.spi_data(spi_data)
		);
		
	//DRAM controller
	wire dram_error;
	MemoryMappedDramController #(.C3_SIMULATION("FALSE")) dram_controller (
			
		.clk(clk), 
		.addr(ram_addr), 
		.wr(ram_wr), 
		.rd(ram_rd), 
		.din(ram_din), 
		.wmask(ram_wmask), 
		.dout(ram_dout), 
		.doutok(ram_dinok),
		
		.clk_20mhz(clk_20mhz_ibufg),
		.clk_ddr_p(clk_ddr_p_raw),
		.clk_ddr_n(clk_ddr_n_raw),
		.clk_mcb_calib(clk_mcb_calib),
		.clk_pll_reset(main_clk_pll_reset),
		.clk_pll_locked(main_clk_pll_locked),
		
		.mcb3_dram_dq(mcb3_dram_dq), 
		.mcb3_dram_a(mcb3_dram_a), 
		.mcb3_dram_ba(mcb3_dram_ba), 
		.mcb3_dram_cke(mcb3_dram_cke), 
		.mcb3_dram_ras_n(mcb3_dram_ras_n), 
		.mcb3_dram_cas_n(mcb3_dram_cas_n), 
		.mcb3_dram_we_n(mcb3_dram_we_n), 
		.mcb3_dram_dm(mcb3_dram_dm), 
		.mcb3_rzq(mcb3_rzq), 
		.mcb3_dram_dqs(mcb3_dram_dqs), 
		.mcb3_dram_ck(mcb3_dram_ck), 
		.mcb3_dram_ck_n(mcb3_dram_ck_n), 
		
		.calib_done(dram_calib_done),
		.error(dram_error),
		.fifo_full(ram_fifo_full)
		);
		
	////////////////////////////////////////////////////////////////////////////////////////////////
	// System health monitor
	
	wire[15:0] vbus_3v3;
	wire[15:0] vbus_2v5;
	wire[15:0] vbus_1v2;
	
	wire[15:0] ibus_3v3;
	wire[15:0] ibus_2v5;
	wire[15:0] ibus_1v2;
	
	wire[15:0] pbus_3v3;
	wire[15:0] pbus_2v5;
	wire[15:0] pbus_1v2;
	
	//wire update_interrupt;
	BoardHealthMonitor health_monitor (
		.clk(clk), 
		.i2c_sda(i2c_sda), 
		.i2c_scl(i2c_scl),
		
		.vbus_3v3(vbus_3v3), 
		.vbus_2v5(vbus_2v5), 
		.vbus_1v2(vbus_1v2), 
		.ibus_3v3(ibus_3v3), 
		.ibus_2v5(ibus_2v5), 
		.ibus_1v2(ibus_1v2), 
		.pbus_3v3(pbus_3v3), 
		.pbus_2v5(pbus_2v5), 
		.pbus_1v2(pbus_1v2)//, 
		
		//.update_interrupt(update_interrupt)
		);
		
	assign gpio[7:0] = 8'hFF;
	assign gpio[19:10] = 10'hzzz;

	BoardHealthMonitor_UARTWrapper health_uart_wrapper (
		.clk(clk), 
		.vbus_3v3(vbus_3v3), 
		.vbus_2v5(vbus_2v5), 
		.vbus_1v2(vbus_1v2), 
		.ibus_3v3(ibus_3v3), 
		.ibus_2v5(ibus_2v5), 
		.ibus_1v2(ibus_1v2), 
		.pbus_3v3(pbus_3v3), 
		.pbus_2v5(pbus_2v5), 
		.pbus_1v2(pbus_1v2), 
		//.uart_tx(uart_tx), 
		//.uart_rx(uart_rx)
		.uart_tx(gpio[8]), 
		.uart_rx(gpio[9])
		);
		
	assign gpio[9] = 1'bz;

endmodule
